/**
 * AddNewNodes.java
 * ************************** 
 * @date May 30, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package infrastructureAPI.algorithms.instanceTools;

import gossipServices.basic.nodeDescriptors.GeneralNodeDescriptor;
import gossipServices.basic.nodeDescriptors.NodeDescriptor;
import gossipServices.basic.view.ConcretePartialView;
import gossipServices.basic.view.PartialView;
import gossipServices.bootstraping.tman.TManService;
import gossipServices.pss.PeerSamplingServiceAPI;
import infrastructureAPI.Requester;
import infrastructureAPI.Service;
import infrastructureAPI.APIMessages.DispatcherRequestMessage;
import infrastructureAPI.APIMessages.RequestMessage;
import infrastructureAPI.algorithms.instanceTools.facilities.TManStarter;
import infrastructureAPI.backendGate.APIScript;

import java.net.InetAddress;
import java.util.List;
import java.util.Vector;

import networkRmi.NetworkManager;

/**
 * Description: 
 * This is the algorithm implementing the feature to add
 * new nodes to a subcloud. 
 * It takes advantage of the NodeRetrieval class with the retrieval 
 * algorithm.
 * It implements the APIFunction and the APIScript interfaces.
 *
 */
public class AddNewNodes extends NodeRetrieval 
	implements APIScript {
	
	/**
	 * The subcloud id to which add new ndoes.
	 */
	protected String subcloudID;
	
	/**
	 * The number of new nodes to be retrieved
	 * from the network and to be added to 
	 * the subcloud.
	 */
	protected int numberOfNodes;

	protected static int mandatoryParametersNumber = 2;
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * Warning: the default 'empty' constructor is 
	 * generally used to retrieve the mandatory parameters number
	 * and to show the usage message.
	 */
	public AddNewNodes() {}
	
	/**
	 * @param requester
	 */
	public AddNewNodes(Requester requester) {
		super(requester);
	}
	
	public AddNewNodes(Requester requester, String subcloud_id,
			int number_of_nodes){
		super(requester);
		this.subcloudID = subcloud_id;
		this.numberOfNodes = number_of_nodes;
	}
	


	@Override
	public RequestMessage createRequestMessage(InetAddress targetAddress,
			String targetNodeName) {
		return defaultRequestMessageComposition(targetAddress, targetNodeName);
	}

	/**
	 * 
	 * @param existingSubcloud
	 * @return
	 */
	protected List<NodeDescriptor> extractNodesFromView(
			PartialView view) {
		/// !!! very naive function !!! ///
		List<NodeDescriptor> vec = new Vector<NodeDescriptor>();
		for(int i=0; i< view.size(); i++ ){
			vec.add(view.getAt(i));
		}
			
		return vec;
	}

	@Override
	public Service extractServiceToDeRegister() {
		return null;
	}

	@Override
	public Service extractSuppliedService() {
		return null;
	}

	@Override
	public boolean requireSystemDeRegistration() {
		return false;
	}

	@Override
	public boolean requireSystemRegistration() {
		return false;
	}

	@Override
	public List<Class<?>> requiredServices() {
		List<Class<?>> list = new Vector<Class<?>>();
		list.add(NetworkManager.class);
		list.add(PeerSamplingServiceAPI.class);
		list.add(TManService.class);
		return list;
	}

	@Override
	public int mandatoryParametersNumber() {
		return mandatoryParametersNumber;
	}

	@Override
	public String showUsage() {
		String usage = new String("");
		usage += "add-new-nodes <subcloud_id> <number>\n";
		usage += "subcloud_id= the id of the target subcloud to which add new nodes\n";
		usage += "number= number of nodes to add into the input subcloud";
		return usage;
	}

	@Override
	public boolean requireReply() {
		return true;
	}

	@Override
	public void run() {
		boolean correctID;
		
		// retrieve services:
		// ------------------
		NetworkManager netMan = (NetworkManager) services.get(0);
		PeerSamplingServiceAPI pss =(PeerSamplingServiceAPI) services.get(1);
		TManService tmanService = (TManService) services.get(2);
		
		// get the current running overlay
		// ------------------
		PartialView existingSubcloud = tmanService.getOverlayNeighbors();
		
		// check the subcloud id:
		// ------------------
		correctID = tmanService.getSubcloudID().equals(subcloudID);
		
		if(correctID){

			List<NodeDescriptor> vec = extractNodesFromView(existingSubcloud);

			// retrieve the local node NodeDescriptor
			// ------------------
			NodeDescriptor localNode = new GeneralNodeDescriptor(netMan.getNodeName());

			// the current overlay doesn't contain the local node. We have
			// to add it to vec in order to don't pickup again from PSS 
			// ------------------
			vec.add(localNode);

			// retrieve nodes from the PeerSamplingService
			// ------------------
			List<NodeDescriptor> updatedVec = retrieveNodesFromPeerSamplingService(
					netMan, pss, numberOfNodes, localNode, vec);

			PartialView enlargedSubcloud = new ConcretePartialView(updatedVec);
			RequestMessage propagatingRequestMessage = 
				new DispatcherRequestMessage(localNode, null, null, requester);

			PartialView toSend = new ConcretePartialView(numberOfNodes);
			toSend.append(enlargedSubcloud);
			toSend.removeHead(toSend.size() - numberOfNodes);
			System.out.println(this.getClass().getSimpleName()+" sending to:\n"+toSend);

			TManStarter tmanStarter = new TManStarter(netMan, enlargedSubcloud, enlargedSubcloud.size(), 
					subcloudID, propagatingRequestMessage, toSend);

			returnValue = (tmanStarter != null);
		}else{
			System.out.println(this.getClass().getSimpleName()+" " +
					"WARNING: inserted a wrong subcloudID !!");
			returnValue = false;
		}
		
	}

}
